<template>
	<view class="m-cascader" :class="{ 'is-readonly': readonly }">
		<!-- 'is-disabled': disabled, -->
		<!-- 输入框触发器 -->
		<view class="m-cascader__input" :class="{ 'has-border': showBorder }">
			<view class="m-cascader__label" :class="{ 'is-placeholder': !selectedLabels.length }" @click="handleInputClick">
				<slot>
					<template v-if="multiple">
						<view v-if="selectedLabels.length" class="m-tag-group">
							<view v-for="(tag, index) in selectedLabels" :key="index" class="m-tag">
								<view class="m-tag__text">{{ tag }}</view>
								<view v-if="!disabled && !readonly" class="m-tag__close" @click.stop="handleTagClose(index)">
									<uni-icons type="clear" size="14" color="#999"></uni-icons>
								</view>
							</view>
						</view>
						<view v-else class="is-placeholder">
							<view>{{ placeholder }}</view>
							<!-- <image v-if="!disabled" src="/static/images/common/down.svg" class="down-img"></image> -->
						</view>
					</template>
					<template v-else>
						<view v-if="selectedLabel" style="display: flex; align-items: center; justify-content: space-between; width: 100%">
							<text class="m-tag__text" style="color: #000;">{{ selectedLabel }}</text>
							<!-- <image v-if="!disabled" src="/static/images/common/down.svg" class="down-img"></image> -->
						</view>
						<view v-else class="is-placeholder">
							<view>{{ placeholder }}</view>
							<!-- <image v-if="!disabled" src="/static/images/common/down.svg" class="down-img"></image> -->
						</view>
					</template>
				</slot>
			</view>
			<!-- <view v-if="!disabled && !readonly" class="m-cascader__icons">
				<uni-icons v-if="hasValue" type="clear" size="20" color="#999" @click.stop="handleClearAll"></uni-icons>
				<uni-icons v-else :type="showPopup ? 'top' : 'bottom'" size="14" color="#999"></uni-icons>
			</view> -->
		</view>

		<!-- 弹出层 -->
		<uni-popup ref="popup" type="bottom" @change="onPopupChange" @maskClick="handleMaskClick">
			<view class="m-cascader__popup">
				<view class="m-cascader__header">
					<view class="m-cascader__close" @click="handleClose">
						<uni-icons type="closeempty" size="20" color="#999"></uni-icons>
					</view>
					<text class="m-cascader__title">{{ title }}</text>
					<view v-if="multiple" class="m-cascader__confirm" @click="handleConfirm">
						<text>确定</text>
					</view>
				</view>

				<view class="m-cascader__content">
					<scroll-view class="m-cascader__menu" scroll-y v-for="(menu, index) in menus" :key="index">
						<view
							class="m-cascader__item"
							v-for="item in menu"
							:key="item[valueKey]"
							:class="{
								'is-active': isSelected(item, index)
							}"
							@click="handleNodeClick(item, index)"
						>
							<!-- 'is-disabled': item.disabled -->
							<text class="m-cascader__label">{{ item[labelKey] }}</text>
							<view v-if="item[childrenKey] && item[childrenKey].length" class="m-cascader__expand">
								<uni-icons type="right" size="14" color="#999"></uni-icons>
							</view>
							<text v-if="multiple && isSelected(item, index)" class="m-cascader__checkbox">✓</text>
						</view>
					</scroll-view>
				</view>
			</view>
		</uni-popup>
	</view>
</template>

<script>
export default {
	name: 'MCascader',
	props: {
		options: {
			type: Array,
			default: () => []
		},
		modelValue: {
			type: [Array, String, Number],
			default: ''
		},
		fieldProps: {
			type: Object,
			default: () => ({})
		},
		placeholder: {
			type: String,
			default: '请选择'
		},
		title: {
			type: String,
			default: '请选择'
		},
		multiple: {
			type: Boolean,
			default: false
		},
		saveFullPath: {
			type: Boolean,
			default: false
		},
		disabled: {
			type: Boolean,
			default: false
		},
		showBorder: {
			type: Boolean,
			default: true
		},
		readonly: {
			type: Boolean,
			default: false
		}
	},

	data() {
		return {
			showPopup: false,
			menus: [this.options], // 确保初始时包含 options
			selectedNodes: [],
			tempSelectedNodes: [],
			valueKey: this.fieldProps.value || 'value',
			labelKey: this.fieldProps.label || 'label',
			childrenKey: this.fieldProps.children || 'children',
			flattenedData: [],
			pathMap: new Map()
		};
	},

	computed: {
		selectedLabel() {
			if (!this.selectedNodes.length) return '';
			if (this.multiple) return '';

			const value = Array.isArray(this.selectedNodes[0]) ? this.selectedNodes[0][this.selectedNodes[0].length - 1] : this.selectedNodes[0];
			const pathInfo = this.getFullPathInfo(value);
			return pathInfo ? pathInfo.fullPathName : '';
		},

		selectedLabels() {
			if (!this.selectedNodes.length) return [];
			if (!this.multiple) return [this.selectedLabel];

			return this.selectedNodes.map((node) => {
				const value = Array.isArray(node) ? node[node.length - 1] : node;
				const pathInfo = this.getFullPathInfo(value);
				return pathInfo ? pathInfo.fullPathName : '';
			});
		},
		hasValue() {
			if (this.multiple) {
				return this.selectedNodes.length > 0;
			}
			return this.modelValue && (Array.isArray(this.modelValue) ? this.modelValue.length > 0 : true);
		}
	},

	watch: {
		modelValue: {
			handler(val) {
				this.initSelected();
			},
			immediate: true
		},
		options: {
			handler(val) {
				this.initData();
			},
			immediate: true
		}
	},
	mounted() {
		this.initData();
		this.initSelected();
	},
	methods: {
		handleClearAll(e) {
			e.stopPropagation();
			this.selectedNodes = [];
			this.tempSelectedNodes = [];
			this.menus = [this.options];
			this.handleChange();
		},
		handleInputClick() {
			if (!this.disabled && !this.readonly) {
				this.togglePopup();
			}
		},
		togglePopup() {
			if (this.showPopup) {
				this.handleClose();
			} else {
				this.showPopup = true;
				this.$refs.popup.open();
				if (this.multiple) {
					this.tempSelectedNodes = JSON.parse(JSON.stringify(this.selectedNodes));
				}
			}
		},
		handleClose() {
			this.showPopup = false;
			this.$refs.popup.close();
			if (this.multiple) {
				this.tempSelectedNodes = JSON.parse(JSON.stringify(this.selectedNodes));
			}
		},
		onPopupChange(e) {
			this.showPopup = e.show;
		},
		handleTagClose(index) {
			this.tempSelectedNodes.splice(index, 1);
			this.selectedNodes = [...this.tempSelectedNodes];
			this.handleChange();
		},
		handleClear() {
			this.tempSelectedNodes = [];
			this.selectedNodes = [];
			this.handleChange();
		},
		handleNodeClick(item, menuIndex) {
			if (item.disabled) return;

			if (this.multiple) {
				this.handleMultiSelect(item, menuIndex);
			} else {
				this.handleSingleSelect(item, menuIndex);
			}
		},
		handleMultiSelect(item, menuIndex) {
			if (item[this.childrenKey] && item[this.childrenKey].length) {
				this.$set(this.menus, menuIndex + 1, item[this.childrenKey]);
				this.menus.length = menuIndex + 2;
				return;
			}

			const currentPath = [];
			let currentNode = item;
			let currentIndex = menuIndex;

			while (currentIndex >= 0) {
				const menu = this.menus[currentIndex];
				const selectedItem = menu.find((node) =>
					currentIndex === menuIndex
						? node[this.valueKey] === item[this.valueKey]
						: node[this.childrenKey]?.some((child) => child[this.valueKey] === currentNode[this.valueKey])
				);

				if (selectedItem) {
					currentPath.unshift(selectedItem[this.valueKey]);
					currentNode = selectedItem;
				}
				currentIndex--;
			}

			const index = this.tempSelectedNodes.findIndex((node) => {
				if (Array.isArray(node)) {
					return node.join(',') === currentPath.join(',');
				}
				return node === item[this.valueKey];
			});

			if (index > -1) {
				this.tempSelectedNodes.splice(index, 1);
			} else {
				this.tempSelectedNodes.push(this.saveFullPath ? currentPath : item[this.valueKey]);
			}
		},
		handleSingleSelect(item, menuIndex) {
			if (item[this.childrenKey] && item[this.childrenKey].length) {
				const currentPath = [];
				let currentNode = item;
				let currentIndex = menuIndex;

				while (currentIndex >= 0) {
					const menu = this.menus[currentIndex];
					const selectedItem = menu.find((node) =>
						currentIndex === menuIndex
							? node[this.valueKey] === item[this.valueKey]
							: node[this.childrenKey]?.some((child) => child[this.valueKey] === currentNode[this.valueKey])
					);

					if (selectedItem) {
						currentPath.unshift(selectedItem[this.valueKey]);
						currentNode = selectedItem;
					}
					currentIndex--;
				}

				this.selectedNodes = [this.saveFullPath ? currentPath : item[this.valueKey]];
				this.$set(this.menus, menuIndex + 1, item[this.childrenKey]);
				this.menus.length = menuIndex + 2;
				return;
			}

			const currentPath = [];
			let currentNode = item;
			let currentIndex = menuIndex;

			while (currentIndex >= 0) {
				const menu = this.menus[currentIndex];
				const selectedItem = menu.find((node) =>
					currentIndex === menuIndex
						? node[this.valueKey] === item[this.valueKey]
						: node[this.childrenKey]?.some((child) => child[this.valueKey] === currentNode[this.valueKey])
				);

				if (selectedItem) {
					currentPath.unshift(selectedItem[this.valueKey]);
					currentNode = selectedItem;
				}
				currentIndex--;
			}

			this.selectedNodes = [this.saveFullPath ? currentPath : item[this.valueKey]];
			this.handleChange();
			this.handleClose();
		},
		isSelected(item, menuIndex) {
			if (this.multiple) {
				return this.tempSelectedNodes.some((node) => {
					if (Array.isArray(node)) {
						return node[menuIndex] === item[this.valueKey];
					}
					return node === item[this.valueKey];
				});
			} else {
				if (!this.selectedNodes.length) return false;
				const selectedPath = Array.isArray(this.selectedNodes[0]) ? this.selectedNodes[0] : [this.selectedNodes[0]];
				return selectedPath[menuIndex] === item[this.valueKey];
			}
		},
		handleConfirm() {
			this.selectedNodes = [...this.tempSelectedNodes];
			this.handleChange();
			this.handleClose();
		},
		handleChange() {
			let value;
			if (this.multiple) {
				if (this.saveFullPath) {
					value = this.selectedNodes.map((node) => (Array.isArray(node) ? node : [node]));
				} else {
					value = this.selectedNodes.map((node) => {
						if (Array.isArray(node)) {
							return node[node.length - 1];
						}
						return node;
					});
				}
			} else {
				value = this.selectedNodes.length ? this.selectedNodes[0] : undefined;
			}
			this.$emit('update:modelValue', value);
			this.$emit('change', value);
		},
		initSelected() {
			if (this.multiple) {
				if (Array.isArray(this.modelValue)) {
					this.selectedNodes = this.modelValue.map((item) => {
						return this.saveFullPath ? (Array.isArray(item) ? [...item] : [item]) : Array.isArray(item) ? item[item.length - 1] : item;
					});
					this.tempSelectedNodes = JSON.parse(JSON.stringify(this.selectedNodes));
				} else {
					this.selectedNodes = [];
					this.tempSelectedNodes = [];
				}
			} else {
				if (this.saveFullPath && Array.isArray(this.modelValue)) {
					let currentOptions = this.options;
					let path = [];
					let nodes = [];
					let lastNode = null;

					for (const value of this.modelValue) {
						const node = currentOptions.find((item) => item[this.valueKey] === value);
						if (node) {
							if (lastNode) {
								node.parent = lastNode;
							}
							nodes.push(node);
							path.push(value);
							lastNode = node;
							currentOptions = node[this.childrenKey] || [];
						}
					}
					if (path.length === this.modelValue.length) {
						this.selectedNodes = [path];
						this.menus = [this.options];
						for (let i = 0; i < nodes.length - 1; i++) {
							if (nodes[i][this.childrenKey]) {
								this.menus.push(nodes[i][this.childrenKey]);
							}
						}
					} else {
						this.selectedNodes = [];
						this.menus = [this.options];
					}
				} else {
					this.selectedNodes = this.modelValue ? [this.modelValue] : [];
					this.menus = [this.options];
				}
			}
		},
		flattenAndMapData(data, parentPath = [], parentLabels = []) {
			let result = [];
			data.forEach((item) => {
				const currentPath = [...parentPath, item[this.valueKey]];
				const currentLabels = [...parentLabels, item[this.labelKey]];
				const fullPathName = currentLabels.join(' / ');

				result.push({
					...item,
					fullPathName,
					fullPath: currentPath
				});

				this.pathMap.set(item[this.valueKey], {
					path: currentPath,
					labels: currentLabels,
					fullPathName
				});

				if (item[this.childrenKey] && item[this.childrenKey].length) {
					result = result.concat(this.flattenAndMapData(item[this.childrenKey], currentPath, currentLabels));
				}
			});
			return result;
		},
		getFullPathInfo(value) {
			return this.pathMap.get(value);
		},
		initData() {
			this.pathMap.clear();
			this.flattenedData = this.flattenAndMapData(this.options);
			this.menus = [this.options]; // 确保初始时包含 options
		},
		handleMaskClick() {
			if (this.multiple) {
				this.selectedNodes = [...this.tempSelectedNodes];
			}
			this.handleChange();
			this.handleClose();
		}
	}
};
</script>

<style lang="scss">

.is-placeholder {
	font-family: PingFang SC, PingFang SC;
	font-size: 28rpx;
	color: #999999;
	line-height: 33rpx;
	text-align: left;
	font-style: normal;
	text-transform: none;

	display: flex;
	align-items: center;
	justify-content: space-between;
}
.m-cascader {
	&.is-disabled {
		.m-cascader__input {
			cursor: not-allowed;
			background-color: #f5f7fa;
			border-color: #e4e7ed;
		}

		.m-cascader__label {
			cursor: not-allowed;
			color: #c0c4cc;
		}

		.m-tag {
			background-color: #f5f7fa;
			border-color: #e4e7ed;
			color: #c0c4cc;
		}
	}

	&.is-readonly {
		.m-cascader__input {
			cursor: default;
			background-color: transparent;
			border-color: transparent;
			padding: 0;

			&:hover {
				border-color: transparent;
			}
		}

		.m-cascader__label {
			color: #000;
			font-size: 16px;
		}

		.m-tag {
			background-color: transparent;
			border: none;
			padding: 2px 4px;
			font-size: 14px;
			color: #000;
		}
	}

	&__input {
		position: relative;
		width: 100%;
		min-height: 35px;
		// padding: 4px 10px;
		padding-left: 0;
		box-sizing: border-box;
		font-size: 14px;
		border-radius: 4px;
		cursor: pointer;
		transition: all 0.3s;
		display: flex;
		align-items: center;

		&.has-border {
			&:hover {
				border-color: #409eff;
			}
		}

		&.is-disabled {
			background-color: #f5f7fa;
			border-color: #e4e7ed;
			color: #c0c4cc;
			cursor: not-allowed;

			&:hover {
				border-color: #e4e7ed;
			}
		}
	}

	&__label {
		flex: 1;
		display: flex;
		flex-wrap: wrap;
		align-items: center;
		padding: 3px 0;

		&.is-placeholder {
			font-family: PingFang SC, PingFang SC;
			font-size: 28rpx;
			color: #999999;
			line-height: 33rpx;
			text-align: left;
			font-style: normal;
			text-transform: none;

			display: flex;
			align-items: center;
			justify-content: space-between;
		}
	}

	&__popup {
		background-color: #fff;
		border-radius: 10px 10px 0 0;
	}

	&__header {
		position: relative;
		display: flex;
		align-items: center;
		justify-content: center;
		height: 50px;
		border-bottom: 1px solid #eee;
	}

	&__close {
		position: absolute;
		left: 0;
		top: 50%;
		transform: translateY(-50%);
		padding: 15px;
		cursor: pointer;
	}

	&__title {
		font-size: 16px;
		color: #333;
	}

	&__confirm {
		position: absolute;
		right: 0;
		top: 50%;
		transform: translateY(-50%);
		padding: 15px;
		color: #409eff;
		font-size: 14px;
		cursor: pointer;
	}

	&__content {
		display: flex;
		height: 400px;
	}

	&__menu {
		flex: 1;
		border-right: 1px solid #eee;

		&:last-child {
			border-right: none;
		}
	}

	&__item {
		display: flex;
		align-items: center;
		justify-content: space-between;
		height: 34px;
		padding: 0 15px;
		font-size: 14px;
		color: #606266;

		&.is-active {
			color: #409eff;
			font-weight: bold;
		}

		&.is-disabled {
			color: #c0c4cc;
			cursor: not-allowed;
		}
	}

	&__expand {
		color: #c0c4cc;
		font-size: 12px;
	}

	&__checkbox {
		color: #409eff;
		font-size: 14px;
	}

	&__footer {
		display: none;
	}

	&__btn {
		display: none;
	}

	&__icons {
		display: flex;
		align-items: center;
		padding-left: 10px;
	}
}

.m-tag {
	display: inline-flex;
	align-items: center;
	margin-right: 4px;
	padding: 0 4px;
	font-size: 12px;
	height: 24px;
	line-height: 24px;
	border-radius: 4px;
	background-color: #f0f2f5;
	margin-bottom: 5px;

	&__text {
		max-width: 100%;
		overflow: hidden;
		text-overflow: ellipsis;
		white-space: nowrap;
	}

	&__close {
		display: flex;
		align-items: center;
		margin-left: 4px;
		cursor: pointer;
	}
}
.down-img {
	width: 24rpx;
	height: 36rpx;
	margin: auto 0;
}
</style>
